﻿using System;
using System.Diagnostics;
using System.Runtime.Remoting.Messaging;
using System.Threading;

namespace ZHello.Async
{
    /// <summary>
    /// 关于线程相关操作
    /// </summary>
    public class AboutThread
    {
        /// <summary>
        /// 设置上下文数据
        /// </summary>
        public static void SetCallContext()
        {
            //设置上下文数据
            CallContext.SetData("k1", "v1");
            CallContext.SetData("k2", 3.1415F);
            ThreadPool.QueueUserWorkItem(state =>
            {
                //此时可以访问到上下文数据
                Console.WriteLine($"Name={CallContext.GetData("k1")}");
                Console.WriteLine($"Name={CallContext.GetData("k2")}");
            });
            //阻止当前线程的上下文流动
            //提升应用程序性能
            ExecutionContext.SuppressFlow();
            ThreadPool.QueueUserWorkItem(state =>
            {
                //此时将访问不到上下文数据
                Console.WriteLine($"Name={CallContext.GetData("k1")}");
                Console.WriteLine($"Name={CallContext.GetData("k2")}");
            });
        }

        /*
         公共语言运行时的某些宿主（如 Microsoft SQL Server 2005）提供其自己的线程管理。
        提供其自己的线程管理的主机可随时将正在执行的任务从一个物理操作系统线程移至另一个线程。 大多数任务不受此切换的影响。
        但是，某些任务具有线程关联，即它们依赖于物理操作系统线程的标识。 这些任务在执行不应切换的代码时必须通知宿主。
        例如，如果应用程序调用系统 API 来获取具有线程关联的操作系统锁（如 Win32 CRITICAL_SECTION），
        则必须在 BeginThreadAffinity 获取锁之前调用，并 EndThreadAffinity 在释放锁后调用。
        如果在 SQL Server 2005 下运行的代码中使用此方法，则需要在最高的主机保护级别上运行代码。
         */

        /// <summary>
        /// 通知主机托管代码将要执行依赖于当前物理操作系统线程的标识的指令
        /// </summary>
        public static void AboutThreadAffinity()
        {
            Thread.BeginThreadAffinity();
            //该代码必须使用当前物理操作系统线程来执行
            //P/Invoke Native Code
            Thread.EndThreadAffinity();
        }

        /// <summary>
        /// 验证Thread.Abort()和Join()
        /// </summary>
        public static void AboutThreadAbort()
        {
            var th1 = new Thread(new ThreadStart(() =>
            {
                while (true)
                {
                    try
                    {
                        Console.WriteLine("Th1:\t" + Environment.TickCount);
                        Thread.Sleep(100);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
            }));
            var th2 = new Thread(new ThreadStart(() =>
            {
                try
                {
                    while (true)
                    {
                        Console.WriteLine("Th1:\t" + Environment.TickCount);
                        Thread.Sleep(100);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }));
            th1.Start();
            th2.Start();
            //线程内抛出异常停止
            th1.Abort();
            th2.Abort();
            //等待线程执行完成
            th1.Join();
            th2.Join();
        }

        public static void AboutThreadLocal()
        {
            ThreadLocal<int> count = new ThreadLocal<int>();
            ThreadLocal<struct1> st = new ThreadLocal<struct1>();
            ThreadLocal<Obj> objs = new ThreadLocal<Obj>(() => { return new Obj(); });
        }
    }

    public struct struct1
    {
        public int A { get; set; }
        public string Str { get; set; }
    }

    public class Obj
    {
        public int A { get; set; } = 10;
        public string Str { get; set; } = "ok";
        public struct1 stru { get; set; } = new struct1 { A = 20, Str = "no" };
        public Obj()
        {
        }
    }

    /// <summary>
    /// 数据的线程本地存储
    /// </summary>
    public static class ThreadLocalA
    {
        public static ThreadLocal<int> count = new ThreadLocal<int>();
        public static ThreadLocal<struct1> st = new ThreadLocal<struct1>();
        public static ThreadLocal<Obj> objs = new ThreadLocal<Obj>(() => { return new Obj(); });

        public static void Run()
        {
            Thread th1 = new Thread(() =>
            {
                for (int i = 0; i < 1 << 20; i++)
                {
                    count.Value++;
                    struct1 stt = st.Value;
                    stt.A++;
                    stt.Str += i;
                    st.Value = stt;
                    Obj o = objs.Value;
                    o.Str = "s" + i;
                    Thread.Sleep(67);
                    Trace.WriteLine("th1:" + objs.Value.Str);
                }
            });

            Thread th2 = new Thread(() =>
            {
                for (int i = 0; i < 1 << 20; i++)
                {
                    count.Value++;
                    struct1 stt = st.Value;
                    stt.A++;
                    stt.Str += i;
                    st.Value = stt;
                    Obj o = objs.Value;
                    o.Str = "X" + i;
                    Thread.Sleep(7);
                    Trace.WriteLine("th2:" + objs.Value.Str);
                }
            });

            th1.Start();
            Thread.Sleep(60);
            th2.Start();

            Thread.Sleep(100);
            Trace.WriteLine("Main:" + objs.Value.Str);
        }
    }
}